////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// The MIT License (MIT)                                                      //
//                                                                            //
// Copyright (c) 2013 Charles Teague                                          //
//                                                                            //
// Permission is hereby granted, free of charge, to any person obtaining a    //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation  //
// the rights to use, copy, modify, merge, publish, distribute, sublicense,   //
// and/or sell copies of the Software, and to permit persons to whom the      //
// Software is furnished to do so, subject to the following conditions:       //
//                                                                            //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software.                        //
//                                                                            //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        //
// DEALINGS IN THE SOFTWARE.                                                  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Xml;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Configuration;

public class MailerConfigurator
{
    private Dictionary<string, object> properties;
    private Dictionary<string, object> objects;

    public static readonly string BaseTemplatePath = "BaseTemplatePath";
    public static readonly string TemplatePath = "TemplatePath";
    public static readonly string BaseTemplate = "BaseTemplate";
    public static readonly string Template = "Template";
    public static readonly string MailFrom = "MailFrom";
    public static readonly string MailTo = "MailTo";
    public static readonly string Subject = "Subject";
    public static readonly string SmtpServer = "SmtpServer";

    public MailerConfigurator() : this(false)
    {
    }

    public MailerConfigurator(bool loadDefaults)
    {
        properties = new Dictionary<string, object>();
        objects = new Dictionary<string,object>();
        if (loadDefaults)
        {
			LoadDefaults();
        }
    }	
    
    public void LoadDefaults()
    {
		properties[SmtpServer] = ConfigurationManager.AppSettings["SmtpServer"];
		properties[MailFrom] = ConfigurationManager.AppSettings["MailFrom"];
    }
    
    public void AddProperty(string key, string value)
    {
		if (!properties.ContainsKey(key))
		{
			properties.Add(key, value);
		}
		else
		{
			properties[key] = value;
		}
    }
    
    public void AddObject(string key, object value)
    {
		if (!objects.ContainsKey(key))
		{
			objects.Add(key, value);
		}
		else
		{
			objects[key] = value;
		}
    }

    public Dictionary<string, object> Properties
    {
        get
        { 
			return properties;
		}
    }

	public Dictionary<string, object> Objects
	{
		get
		{
			return objects;
		}
	}

    internal void Check()
    {
        if (Properties[SmtpServer] == null)
		{
			throw new ArgumentException("Smtp Server doesn't exist.");
		}
		else if (Properties[MailTo] == null)
		{
			throw new ArgumentException("MailTo doesn't exist.");
		}
    }
}

public class Mailer
{
    private const string beginTemplateChar = "<%";
    private const string endTemplateChar = "%>";
	private const string holderTemplate = "PAGECONTENT";

    private Dictionary<string, object> objects;
    private Dictionary<string, object> properties;
    //private List<Attachment> attachments;
    private Attachment attachments;
        
    public Mailer(MailerConfigurator cfg)
    {
        cfg.Check();

        properties = cfg.Properties;
        objects = cfg.Objects;
        //attachments = new Attachment("");
    }
   
    public void SendMail()
    {
        try
        {
            string strBaseTemplate = properties.ContainsKey(MailerConfigurator.BaseTemplate) ?
				properties[MailerConfigurator.BaseTemplate].ToString() : GetTemplateText(properties[MailerConfigurator.BaseTemplatePath].ToString());

            string strTemplate = properties.ContainsKey(MailerConfigurator.Template) ?
                properties[MailerConfigurator.Template].ToString() : GetTemplateText(properties[MailerConfigurator.TemplatePath].ToString());

			strTemplate = strBaseTemplate.Replace(beginTemplateChar + holderTemplate + endTemplateChar, strTemplate);
            BindData(ref strTemplate);
            
            SendMail(properties, strTemplate, attachments);
        }
        catch (Exception ex)
        {
            throw new MailerException(ex.Message, ex);
        }
    }
    public void SendMailWithAttachment(Attachment attach)
    {
        try
        {
            string strBaseTemplate = properties.ContainsKey(MailerConfigurator.BaseTemplate) ?
                properties[MailerConfigurator.BaseTemplate].ToString() : GetTemplateText(properties[MailerConfigurator.BaseTemplatePath].ToString());

            string strTemplate = properties.ContainsKey(MailerConfigurator.Template) ?
                properties[MailerConfigurator.Template].ToString() : GetTemplateText(properties[MailerConfigurator.TemplatePath].ToString());

            strTemplate = strBaseTemplate.Replace(beginTemplateChar + holderTemplate + endTemplateChar, strTemplate);
            BindData(ref strTemplate);

            SendMail(properties, strTemplate, attach);
        }
        catch (Exception ex)
        {
            throw new MailerException(ex.Message, ex);
        }
    }
    private static void SendMail(Dictionary<string, object> properties, string msg, Attachment attach)
    {
        MailMessage mail = new MailMessage(properties[MailerConfigurator.MailFrom].ToString(), 
			properties[MailerConfigurator.MailTo].ToString());
                
        mail.Body = msg;
        mail.IsBodyHtml = true;
        mail.Subject = properties.ContainsKey(MailerConfigurator.Subject) && properties[MailerConfigurator.Subject] != null ?
            properties[MailerConfigurator.Subject].ToString() : String.Empty;

        if (attach != null)
            mail.Attachments.Add(attach);
        

        SmtpClient client = new SmtpClient(properties[MailerConfigurator.SmtpServer].ToString());
		try
		{
			client.Send(mail);
            mail.Dispose();
		}
		catch (SmtpFailedRecipientsException ex)
		{
			throw new MailerException(ex);
		}
    }

    private string GetTemplateText(string xmlTemplatePath)
    {
        XmlDocument doc = new XmlDocument();
		doc.Load(System.Web.Hosting.HostingEnvironment.MapPath(xmlTemplatePath));
        XmlElement root = doc.DocumentElement;
        XmlNode nodeTemplate = root.SelectSingleNode("//Email/PageTemplate");
		
		XmlNode subjectNode = root.SelectSingleNode("//Email/Subject");
		if (subjectNode != null &&
			!properties.ContainsKey(MailerConfigurator.Subject))
		{
			string subject = subjectNode.InnerText.Trim();
			BindData(ref subject);
			properties.Add(MailerConfigurator.Subject, subject);
		}

        return nodeTemplate.InnerText;
    }

	private void BindData(ref string container)
    {
        int startPos = 0;
        int endPos = 0;
		List<string> templates = new List<string>();
		
        while (true)
        {
            startPos = container.IndexOf(beginTemplateChar, startPos);
            if (startPos == -1)
			{
				break;
			}
			
            endPos = container.IndexOf(endTemplateChar, startPos);
            startPos += beginTemplateChar.Length;
            templates.Add(container.Substring(startPos, endPos - startPos).Trim());
        }
        
        foreach (string name in templates)
        {
			if (name.IndexOf('.') != -1)
			{
				string[] property = name.Split('.');
				string value = GetProperty(property[0], property[1]);
				container = container.Replace(beginTemplateChar + name + endTemplateChar, value);
			}
        }
    }
    
    private string GetProperty(string objectName, string propertyName)
    {
		string result = null;
		if (objects.ContainsKey(objectName))
		{
			object obj = objects[objectName];
			PropertyInfo pi = obj.GetType().GetProperty(propertyName);
			if (pi != null)
			{
				result = pi.GetValue(obj, null).ToString();
                //result = pi.GetValue(obj, null);

			}
		}
		
		return result == null ? "<NULL>" : result;
    }
}
/*
public sealed class MailerUtils
{
    public static List<string> GetAllImagePathFromHTML(string htmlContent)
    {
        List<string> path = new List<string>(64);
		Regex regex = new Regex(@"img\s+src\s*=\s*[""']?(.*?)[""'>]", RegexOptions.IgnoreCase);

		foreach (Match match in regex.Matches(htmlContent))
			if (!path.Contains(match.Groups[1].Captures[0].Value))
				path.Add(match.Groups[1].Captures[0].Value);

        return path;
    }

    public static string ReplaceImagePathInHTML(string htmlContent, string imagePath1, string imagePath2)
    {
        return htmlContent.Replace(imagePath1, imagePath2);
    }

    public static string ReplaceImagePathInHTML(string htmlContent, List<string> imagePath)
    {
        foreach (string path in imagePath)
        {
            string imgPath = path.Substring(path.LastIndexOf("/") + 1);
            htmlContent = ReplaceImagePathInHTML(htmlContent, path, imgPath);
        }
        return htmlContent;
    }

    public static ArrayList GetAttachmentImages(string htmlContent, string serverName)
    {
        ArrayList attachments = new ArrayList();
        AttachImage image;
        foreach (string imgPath in MailerUtils.GetAllImagePathFromHTML(htmlContent))
        {
            string path = imgPath;
            if (path.IndexOf("http:") != 0 && path.IndexOf(serverName) != 0)
            {
                path = serverName + imgPath;
            }
            System.Drawing.Image img = MailerUtils.GetImageByURL(path);

            if (img != null)
            {
                image.ImageName = imgPath.Substring(path.LastIndexOf("/") + 1);
				image.Image = img;
                attachments.Add(image);
            }
        }
        return attachments;
    }

    public static System.Drawing.Image GetImageByURL(string url)
    {
        try
        {
            System.Net.WebRequest request = System.Net.WebRequest.Create(url);

            System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();

            System.IO.Stream dataStream = response.GetResponseStream();

            System.Drawing.Image image = System.Drawing.Bitmap.FromStream(dataStream);

            return image;
        }
        catch
        {
            return null;
        }
    }
}
*/
public class MailerException : Exception
{
	private string exceptionMsg;

	public MailerException()
		: base()
	{
	}

	public MailerException(string message)
		: base(message)
	{
	}

	public MailerException(string message, System.Exception cause)
		: base(message, cause)
	{
		exceptionMsg = GetStackTraceMessage(cause);
	}

	public MailerException(System.Exception cause)
		: this(cause.Message, cause)
	{
	}

	public string MailerExceptionMessage
	{
		get
		{
			return exceptionMsg;
		}
	}

	private string GetStackTraceMessage(System.Exception cause)
	{
		StringBuilder builder = new StringBuilder(1024);
		builder.Append(cause.Message);
		builder.Append(cause.StackTrace);
		builder.Append("\n\r");
		while (cause.InnerException != null)
		{
			cause = cause.InnerException;
			builder.Append(cause.Message);
			builder.Append(cause.StackTrace);
			builder.Append("\n\r");
		}

		return builder.ToString();
	}
}
/*
public struct AttachImage
{
	public System.Drawing.Image Image;
	public string ImageName;
}
 * */
